package controller

import (
	"encoding/json"
	"fmt"
	"gin/common"
	"gin/queueAmqp"
	"gin/response"
	"gin/service"
	"gin/transcoder/ffmpeg"
	"gin/validate"
	"github.com/disintegration/imaging"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
	"image"
	"mime/multipart"
	"os"
	"strconv"
	"strings"
	"time"
)

var (
	uploadFileKey  = "Filedata"
	ffmpegBinPath  = ""
	ffprobeBinPath = ""
	ImageType      = []interface{}{"gif", "jpeg", "jpg", "bmp", "png"}
	VideoType      = []interface{}{"mov", "mp4", "avi", "wmv", "flv"}
	AudioType      = []interface{}{"mp3", "wav", "aac"}
)

func UploadMedia(ctx *gin.Context) {

	ffmpegBinPath = viper.GetString("ffmpeg.ffmpegBinPath")
	ffprobeBinPath = viper.GetString("ffmpeg.ffprobeBinPath")

	var err error

	_, err = ctx.FormFile(uploadFileKey)
	imageSize := ctx.PostForm("size")

	if err != nil {
		//ignore
		response.Fail(ctx, nil, err.Error())
		return
	} else {
		mediaMetadataItems := make([]validate.MediaMetadata, 0)
		var mediaMetadata validate.MediaMetadata
		for _, file := range ctx.Request.MultipartForm.File["Filedata"] {
			mediaMetadata, err = DealMediaItem(ctx, file, imageSize)
			if err == nil {
				mediaMetadataItems = append(mediaMetadataItems, mediaMetadata)
			}
		}

		if err != nil {
			response.Fail(ctx, nil, err.Error())
		} else {
			response.Success(ctx, gin.H{"data": mediaMetadataItems}, "Upload successful")
		}
	}
}

// 移动文件到临时文件夹
func MoveFileToTempFolder(ctx *gin.Context, File *multipart.FileHeader, fileExt string) (tempPath string, folderName string, err error) {

	// 文件名称
	folderName = common.RandStringBytesMaskImprSrcUnsafe(10) + "_" + common.GenerateMd5(time.Now().String())

	// 临时文件名
	dstName := folderName + "." + fileExt

	// 临时存放地址
	tempPath = viper.GetString("server.staticPath") + "temp/" + dstName

	// 拷贝到对应文件夹
	err = ctx.SaveUploadedFile(File, tempPath)

	return
}

// 处理单个媒体数据
func DealMediaItem(ctx *gin.Context, File *multipart.FileHeader, imageSize string) (mediaMetadata validate.MediaMetadata, err error) {

	// 文件后缀名
	fileExt := common.GetFileExt(File.Filename)

	if common.InArray(fileExt, ImageType) || common.InArray(fileExt, VideoType) || common.InArray(fileExt, AudioType) {

		tempPath, folderName, MoveErr := MoveFileToTempFolder(ctx, File, fileExt)

		if MoveErr != nil {
			err = MoveErr
			return
		}

		// 获取当前文件的md5值
		fileMd5 := common.GetFileMd5(tempPath)

		// 判断当前md5文件是否存在
		checkResult, mediaDBData := service.CheckFileMd5(fileMd5)

		if checkResult {
			// 存在直接返回
			jsonStr, _ := json.Marshal(mediaDBData.Param)
			_ = json.Unmarshal(jsonStr, &mediaMetadata)
			mediaMetadata.MediaId = mediaDBData.Id

			// 存在之间删除临时文件
			err = os.Remove(tempPath)
		} else {
			// 只支持图片、视频、音频处理
			mediaStatus := ""

			// 遍历赋值
			mediaMetadata.Md5 = fileMd5
			mediaMetadata.Md5Name = folderName
			mediaMetadata.Ext = fileExt
			mediaMetadata.FileSize = File.Size

			// 创建文件夹
			folderPath := "static/" + folderName
			err = common.CreateDirectory("./" + folderPath)

			if err != nil {
				return
			}

			// 文件夹名称
			mediaMetadata.Folder = folderName
			mediaMetadata.Path = folderPath + "/"

			if common.InArray(fileExt, ImageType) {
				// 处理图片方法
				mediaMetadata.Type = "image"
				mediaMetadata.NamePrefix = folderName
				mediaMetadata, err = ConvertImage(mediaMetadata, tempPath, imageSize)
				mediaStatus = "yes"
			} else if common.InArray(fileExt, VideoType) {
				// 处理视频方法
				mediaMetadata.Type = "video"
				mediaMetadata, err = ConvertVideo(mediaMetadata, tempPath)
				mediaStatus = "no"
			} else if common.InArray(fileExt, AudioType) {
				// 处理音频方法
				mediaMetadata.Type = "audio"
				mediaMetadata.NamePrefix = folderName
				mediaMetadata, err = ConvertAudio(mediaMetadata, tempPath)
				mediaStatus = "yes"
			}

			if err == nil {
				// 没有错误保存media数据
				addMedia := validate.AddMedia{}
				addMedia.Md5Name = folderName
				addMedia.Md5 = fileMd5
				addMedia.Type = mediaMetadata.Type
				addMedia.Status = mediaStatus
				mediaMetadata.RelativePath = fmt.Sprintf("/static/%s/%s.%s", addMedia.Md5Name, addMedia.Md5Name, mediaMetadata.Ext)
				addMedia.RelativePath = mediaMetadata.RelativePath
				addMedia.Param, _ = json.Marshal(mediaMetadata)
				id, err := service.CreateMedia(addMedia)
				if err != nil {
					return mediaMetadata, err
				}
				mediaMetadata.MediaId = id

			}
		}
	}

	return
}

// 处理图片
func ConvertImage(mediaMetadata validate.MediaMetadata, tempPath string, imageSize string) (imageMetadata validate.MediaMetadata, err error) {
	imageMetadata = mediaMetadata

	// 获取图片宽高
	file, _ := os.Open(tempPath)
	imgConfig, _, imgConfigErr := image.DecodeConfig(file)
	if imgConfigErr != nil {
		err = imgConfigErr
		return
	}

	imageMetadata.Width = imgConfig.Width
	imageMetadata.Height = imgConfig.Height
	_ = file.Close()

	if imageMetadata.Ext == "gif" {
		// gif 格式不处理直接拷贝
		err = os.Rename(tempPath, "./"+mediaMetadata.Path+imageMetadata.Md5Name+"_origin."+imageMetadata.Ext)

		imageMetadata.Size = "origin"
	} else {
		// 加载图片
		img, OpenErr := imaging.Open(tempPath)

		if OpenErr != nil {
			err = OpenErr
			return
		}

		// 保存原图
		err = imaging.Save(img, "./"+mediaMetadata.Path+imageMetadata.Md5Name+"_origin."+imageMetadata.Ext)

		// 处理更多尺寸
		if imageSize != "" {
			imageMetadata.Size = "origin," + imageSize
			imageSizeArray := strings.Split(imageSize, ",")
			var tempW, tempH int
			for index := range imageSizeArray {
				sizeArray := strings.Split(imageSizeArray[index], "x")
				tempW, _ = strconv.Atoi(sizeArray[0])
				tempH, _ = strconv.Atoi(sizeArray[1])
				dstImage := imaging.Fill(img, tempW, tempH, imaging.Center, imaging.Lanczos)
				err = imaging.Save(dstImage, "./"+mediaMetadata.Path+imageMetadata.Md5Name+"_"+imageSizeArray[index]+"."+imageMetadata.Ext)
				if err != nil {
					return
				}
			}
		} else {
			imageMetadata.Size = "origin"
		}

		// 处理完删除临时文件
		err = os.Remove(tempPath)

		if err != nil {
			return
		}
	}

	return
}

// 处理音频
func ConvertAudio(mediaMetadata validate.MediaMetadata, tempPath string) (audioMetadata validate.MediaMetadata, err error) {

	// 音频都转换为mp3
	if mediaMetadata.Ext != viper.GetString("ffmpeg.default_audio_format") {

		ve := validate.ConvertAudioFormat{
			TempPath:   tempPath,
			Path:       mediaMetadata.Path,
			Md5Name:    mediaMetadata.Md5Name,
			FileFormat: viper.GetString("ffmpeg.default_audio_format"),
		}
		newTempPath, err := queueAmqp.HandleExtractAudioDefaultFormat(ve)
		if err != nil {
			return mediaMetadata, err
		}
		// 转换完成后删除源文件
		_ = os.Remove(tempPath)
		tempPath = newTempPath
		mediaMetadata.Ext = viper.GetString("ffmpeg.default_audio_format")
	}
	audioMetadata = mediaMetadata

	err = os.Rename(tempPath, "./"+audioMetadata.Path+audioMetadata.Md5Name+"."+audioMetadata.Ext)

	return
}

// 处理视频
func ConvertVideo(mediaMetadata validate.MediaMetadata, tempPath string) (videoMetadata validate.MediaMetadata, err error) {

	videoMetadata = mediaMetadata

	// 当格式一致 直接返回
	if mediaMetadata.Ext == "mp4" {
		err = os.Rename(tempPath, "./"+mediaMetadata.Path+mediaMetadata.Md5Name+"."+mediaMetadata.Ext)
		return
	}

	ffmpegBinPath = viper.GetString("ffmpeg.ffmpegBinPath")
	ffprobeBinPath = viper.GetString("ffmpeg.ffprobeBinPath")
	// ffmpeg 配置
	ffmpegConf := &ffmpeg.Config{
		FfmpegBinPath:   ffmpegBinPath,
		FfprobeBinPath:  ffprobeBinPath,
		ProgressEnabled: true,
	}

	// 获取当前视频参数
	metadata, err := ffmpeg.New(ffmpegConf).
		Input(tempPath).
		GetMetadata()

	if err != nil {
		return
	}

	// 获取视频第一个流数据
	stream := metadata.Streams[0]

	// 视频时长
	videoMetadata.Height = stream.Height
	videoMetadata.Width = stream.Width
	videoMetadata.Duration = stream.Duration
	RateArray := strings.Split(stream.RFrameRrate, "/")
	videoMetadata.Rate = RateArray[0]
	videoMetadata.Thumb = "/" + videoMetadata.Folder + "/" + videoMetadata.Md5Name + ".jpg"

	// 执行转码,写入队列
	TranscodeVideoParam := validate.TranscodeVideoParam{}
	TranscodeVideoParam.TempPath = tempPath
	TranscodeVideoParam.Duration = videoMetadata.Duration
	TranscodeVideoParam.Path = videoMetadata.Path
	TranscodeVideoParam.Md5Name = videoMetadata.Md5Name
	TranscodeVideoParam.Height = videoMetadata.Height
	TranscodeVideoParam.Width = videoMetadata.Width

	TranscodeVideoParamJson, err := json.Marshal(TranscodeVideoParam)
	if err != nil {
		// Umarshal failed
		return
	}

	queueAmqp.PushTransCodeParam(TranscodeVideoParamJson)

	return
}

// 通过Md5 Name 查找媒体
func SelectMediaByMd5Name(ctx *gin.Context) {
	var mediaMd5sFilter validate.MediaMd5sFilter
	err := ctx.ShouldBindJSON(&mediaMd5sFilter)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	mediaData, err := service.SelectMediaByMd5Name(mediaMd5sFilter.Md5Name)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{"data": mediaData}, "Select successfully.")
}

// 通过Md5 Name 删除媒体
func RemoveMediaByMd5Name(ctx *gin.Context) {
	var mediaMd5sFilter validate.MediaMd5sFilter
	err := ctx.ShouldBindJSON(&mediaMd5sFilter)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	err = service.RemoveMediaByMd5Name(mediaMd5sFilter.Md5Name)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{}, "Delete successfully.")
}
